/* Copyright 2011 Elektrobit Wireless Communications Oy (EB)


  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License version 2 as 
  published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


 *All rights reserved 
 *Version:Elektrobit_Qt_1.0.0_20110305 */


#ifdef C2PLAYER
#include "playerservice.h"
#include <appcommon.h>
#include <servicemanager.h>
#include <QDateTime>
#include <sys/time.h> 
#include <unistd.h>


#define CALIBRATIONTICK 500

void PlaybackCallback(MspWidget* pWidget, InfoType cmdId, void* data)
{
    printf("############# PlaybackCallback  InfoType = %d ################\n", cmdId);
    switch(cmdId)
    {
    case  INFO_INPUT_STREAM_SEEK_END:
        printf("INFO_INPUT_STREAM_SEEK_END\n");
        break;
    case INFO_PLAY_FINISH:
        SERVICE(PlayerService)->playerFinished();
        printf("INFO_PLAY_FINISH\n");
        break;
    case INFO_FP_OUT_OF_BOUNDS:
        printf("INFO_FP_OUT_OF_BOUNDS\n");
        break;
    case INFO_INVALID_VIDEO:
        printf("INFO_INVALID_VIDEO\n");
        break;
    case INFO_INVALID_AUDIO:
        printf("INFO_INVALID_AUDIO\n");
        break;  
    case INFO_FILENOTFOUND:
        printf("NO FILE FOUND\n");
        break;
    case INFO_UNSUPPORTED:
        printf("UNSUPPORTED FILE\n");
        break;
    case INFO_OPENFAILED:
        printf("OPEN FAILED\n");
        break;
    case INFO_FILE_ERROR:
        printf("FILE ERROR\n");
        break;
    default:
        printf("pvrmain - PlaybackCallback unknow  cmd: %d \n", cmdId);
        break;
    }
}

void SetupBaseLayer(MspWidgetConfiguration* avConfig)
{
    OSD_INIT base_init = { C2_OUTPUT_COMPONENT, C2_VMODE_NTSC, 0, BLACK, 0};
    int base_fd = -1;
    struct fb_var_screeninfo var;
    int vport = 0, vmode = 0, cmode = 0,refresh = 0;

    switch(avConfig->avConfiguration.videoResolution)
    {
    case VideoResolution_Auto:
    case VideoResolution_480:
        if (!avConfig->avConfiguration.isProgressive)
        {
            vmode = C2_VMODE_NTSC;
        }
        else
            vmode = C2_VMODE_480P;
        break;
    case VideoResolution_576:
        if (!avConfig->avConfiguration.isProgressive)
        {
            vmode = C2_VMODE_PAL;
        }
        else
            vmode = C2_VMODE_576P;
        break;
    case VideoResolution_720:
        vmode = C2_VMODE_720P;
        refresh = C2_DISP_STD_50HZ_MASK;
        break;
    case VideoResolution_1080:
        vmode = C2_VMODE_1080I;
        refresh = C2_DISP_STD_50HZ_MASK;
        break;
    case VideoResolution_QVGA_NTSC:
    case VideoResolution_QVGA_PAL:
        vmode = C2_VMODE_QVGA;
        break;
    case VideoResolution_VGA:
        vmode = C2_VMODE_VGA;
        break;
    case VideoResolution_SVGA:
        vmode = C2_VMODE_SVGA;
        break;
    case VideoResolution_XGA:
        vmode = C2_VMODE_XGA;
        break;
    case VideoResolution_SXGA:
        vmode = C2_VMODE_SXGA;
        break;
    case VideoResolution_WXGA:
        vmode = C2_VMODE_WXGA;
        break;
    default:
        ERROR("error video resolution!");
        return;
    }

    switch(avConfig->avConfiguration.videoMode)
    {
    case VideoMode_CVBS:
        vport = C2_OUTPUT_COMPOSITE;
        break;
    case VideoMode_SVideo:
        vport = C2_OUTPUT_SVIDEO;
        break;
    case VideoMode_Component_YUV:
        vport = C2_OUTPUT_COMPONENT;
        break;
    case VideoMode_Component_RGB:
        vport = C2_OUTPUT_COMPONENT;
        break;
    case VideoMode_RGB:
        vport = C2_OUTPUT_VIO_A;
        cmode = 1;
        break;
    case VideoMode_HDMI:
        vport = C2_OUTPUT_VIO_B;
        break;
    case VideoMode_VGA:
        vport = C2_OUTPUT_VGA;
        cmode = 1;
        break;
    default:
        ERROR("error video mode!");
        return;
    }

    char *boardType = getenv("C2_BOARD_TYPE");
    if (boardType && !strcmp(boardType, "C2_BOARD_TYPE_CC289"))
    {
        vport = C2_OUTPUT_VIO_A;
    }
    base_init.osd_base_output_dev = vport;
    base_init.osd_base_vmode = vmode;
    base_fd=open("/dev/display", O_RDWR);
    while(base_fd <= 0) {
        ERROR("failed to open /dev/display\n");
        sleep(1);
        base_fd=open("/dev/display", O_RDWR);
    }
    switch(vport)
    {
    case C2_OUTPUT_COMPOSITE:
    case C2_OUTPUT_SVIDEO:
    case C2_OUTPUT_COMPONENT:
        set_base_pfmt_in(&base_init, C2_PFMT_MODE_422_8BIT);
        set_base_pfmt_out(&base_init, C2_PFMT_MODE_444_10BIT);
        break;
    case C2_OUTPUT_VIO_B:
        set_base_pfmt_in(&base_init, C2_PFMT_MODE_422_8BIT);
        set_base_pfmt_out(&base_init, C2_PFMT_MODE_444_8BIT);
        break;
    case C2_OUTPUT_VIO_A:
        set_base_pfmt_in(&base_init, C2_PFMT_MODE_422_8BIT);
        set_base_pfmt_out(&base_init, C2_PFMT_MODE_422_8BIT);
        break;
    case C2_OUTPUT_VGA:
        set_base_pfmt_in(&base_init, C2_PFMT_MODE_422_8BIT);
        set_base_pfmt_csc(&base_init, C2_PFMT_MODE_CSC_YCbCr2RGB, C2_PFMT_MODE_CSC_10BIT);
        break;
    }

    if((VideoResolution_720 == avConfig->avConfiguration.videoResolution
        || VideoResolution_1080 == avConfig->avConfiguration.videoResolution)
        && avConfig->avConfiguration.videoMode != VideoMode_VGA
                && avConfig->avConfiguration.videoSystem != VideoSystem_NTSC)
        {
        set_base_disp_mode(&base_init,refresh);
    }

    if(avConfig->avConfiguration.isJapanStandard)
    {
        set_base_disp_mode(&base_init, C2_DISP_STD_JP_MASK);
    }

    if (ioctl(base_fd, FBIOBASE_INIT, &base_init)) {
        fprintf(stderr, "FBIOBASE_INIT failed fd=%d\n", base_fd);
        close (base_fd);
        return;
    }

    if (ioctl(base_fd, FBIOGET_VSCREENINFO, &var)) {
        ERROR("FBIOGET_VSCREENINFO failed fd=%d\n", base_fd);
        close (base_fd);
        return;
    } else if (var.width != 0) {
        printf("base layer fb w:%d h:%d bpp:%d %sinterlaced\n",
               var.width,
               var.height,
               var.bits_per_pixel,
               var.vmode == FB_VMODE_INTERLACED ? "inter" : "non");
    }
    close (base_fd);
    return;
}

void DisableVideoOutput()
{
#if (BUILD_TARGET != TARGET_LINUX_X86)
    int base_fd = -1;
    base_fd=open("/dev/display", O_RDWR);

    while (base_fd <= 0) {
        ERROR("failed to open /dev/display\n");
        sleep(1);
        base_fd=open("/dev/display", O_RDWR);
    }

    ioctl(base_fd, FBIOBASE_RELEASE, NULL);
    close(base_fd);
#endif//(BUILD_TARGET != TARGET_LINUX_X86)
}


PlayerService::PlayerService(QObject *parent): Service(parent){
    APPINFO<<"constuct PlayerService";
    pPlaybackWidget = NULL;
    mTimer = new QTimer( this );
    isChecked = false;
    connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
    timerID = -1;
}


void PlayerService::onTimer(){
//    qint64 curTime = QDateTime::currentDateTime().toTime_t();
//    APPINFO << curTime*1000 << mStartTime << mSeekTime;   
    struct timeval nowtimeval;
    gettimeofday(&nowtimeval,0);
    qint64 seconds = nowtimeval.tv_sec;
    qint64 curTime = seconds * 1000 + nowtimeval.tv_usec / 1000;
    APPINFO << "curTime => " << curTime;
    qint64 calCurPos = curTime - mStartTime + mSeekTime;
    u64 curPos;
    getPosition(&curPos);
    
    int diff = calCurPos - curPos;
    APPINFO << calCurPos << curPos << diff;
    if(PlayerStop != getStatus() ){
        if(PlayerPause != getStatus()){
            if(diff > CheckTimeCount){
                APPINFO << "seek " << diff;
                seek(diff + 100);
            }else if( diff < 0 - CheckTimeCount && diff >= -20000 ){
                pause();
                APPINFO << "pause " << abs(diff) + 80;
                timerID = startTimer( abs(diff)  + 80 );           
            } else if(diff < -20000) {
                APPINFO << "seek " << diff;
                seek(diff + 100);
            } else {
                APPINFO << isChecked;
                if(isChecked) {
                    return;
                }
                CheckTimeCount = 80;
                APPINFO << isChecked;
                isChecked = true;
//                SERVICE(BackgroundService)->getBackgroundWidget()->hideMessage();
                SERVICE(BackgroundService)->getBackgroundWidget()->setWindowOpacity(0.0);
                SERVICE(BackgroundService)->getBackgroundWidget()->setAttribute(Qt::WA_TranslucentBackground, true);                
                SERVICE(BackgroundService)->getBackgroundWidget()->updateGeometry();
            }
        }
    }else{
        mTimer->stop();
    }
}

void PlayerService::timerEvent(QTimerEvent *event){
    if (event->timerId() == timerID ){
        APPINFO << "pause finished ------------------";
        resume();
        killTimer(timerID);
        timerID = -1;
//        this->isPause = false;
    }
}

int PlayerService::start(){
    initc2();
    return 0;
}

//------------------------------
//初始化视频输出

int PlayerService::initc2()
{
//    pPlaybackWidget = new PlaybackWidget("playback widget",PlaybackCallback);
//    APPINFO << this->getMode()<<this->property("width").toInt()<<this->property("height").toInt();
//    switch(this->getMode())
//    {
//    case VGA:
//        config.avConfiguration.videoPort = VideoPort_VGA;
//        config.avConfiguration.videoMode = VideoMode_VGA;
//        config.avConfiguration.videoResolution = VideoResolution_XGA;
//        break;
//    default:
//        config.avConfiguration.videoPort = VideoPort_VGA;
//        config.avConfiguration.videoMode = VideoMode_VGA;
//        config.avConfiguration.videoResolution = VideoResolution_XGA;
//        break;
//    }
//    //config.avConfiguration.videoSystem = VideoSystem_PAL;
//    config.avConfiguration.videoRefreshRate = VRefreshRate_60Hz;
//    config.avConfiguration.isProgressive = true;
//    config.avConfiguration.screenWidth = this->property("width").toInt();
//    config.avConfiguration.screenHeight = this->property("height").toInt();
//    config.avConfiguration.videoAspectRatio = AspectRatio_4_3;
//    config.avConfiguration.videoAspectRatioConversion =AspectRatioConversion_FullScreen;
//    config.avConfiguration.i2sPort = AudioPort_I2sOutput;

//    APPINFO<<"before setupbaselayer";
////    SetupBaseLayer(&config);
//    APPINFO<<"before setmspwidgetconfigration";
//    pPlaybackWidget->SetMspWidgetConfiguration(&config);
//    APPINFO<<"before initialize";
//    pPlaybackWidget->Initialize();
//    APPINFO<<"after initialize";
    return 0;
}

//-----------------------------
//播放视频文件
//path: 视频文件路径
//返回值：MSP_SUCCESS(0)成功，其它查看OpenFile接口返回值含义

int PlayerService::play(const QString & path)
{
    //OpenFile是非堵塞的，播放结束返回MSP_SUCCESS(0)，并
    //调用回调函数PlaybackCallback
    mPath = path;
    APPINFO << path << pPlaybackWidget;
    return pPlaybackWidget->OpenFile(path.toUtf8().data());
}

//--------------------------------
//返回值0 成功

int PlayerService::playseekfile(qint64 startime, const QString & path, qint64 seektime){
    isChecked = false;
    CheckTimeCount = 40;
    mStartTime = startime;
    mSeekTime = seektime;
    mTimer->stop();
    struct timeval nowtimeval;
    gettimeofday(&nowtimeval,0);
    qint64 seconds = nowtimeval.tv_sec;
    qint64 curTime = seconds * 1000 + nowtimeval.tv_usec / 1000;
    qint64 seekPrecise = curTime - startime + seektime;

    APPINFO << mStartTime << mSeekTime << seekPrecise;
    if(MSP_SUCCESS == play(path)){
        seek((u64)seekPrecise);
        mTimer->start(CALIBRATIONTICK);
        return 0;
     }
     return 1;
}

//------------------------------
//停止播放
//返回值：MSP_SUCCESS(0)成功，其它查看CloseFile接口返回值含义

int PlayerService::stop()
{
    pPlaybackWidget->CloseFile(MEDIA_VIDEO);
    return 0;
}

//------------------------------
//暂停播放
//返回值：MSP_SUCCESS(0)成功，其它查看Pause接口返回值含义

int PlayerService::pause()
{
    return pPlaybackWidget->Pause();
}

//-----------------------------
//恢复播放
//返回值：MSP_SUCCESS(0)成功，其它查看Play接口返回值含义

int PlayerService::resume()
{
    pPlaybackWidget->Play();
    return 0;
}

//------------------------------
//快进

int PlayerService::forward(bool fast)
{
    //printf("forward GetStatus:%d\n",getStatus());
    if(PlayerRunning == getStatus()){
        if(true == fast){
            pPlaybackWidget->Scan(PLAYBACK_RATE_FF);
        }
        else{
            pPlaybackWidget->Scan(PLAYBACK_RATE_SF);
        }
        //VFD_PLAY_MODE_PLAY
    }
    return 0;
}

//------------------------------
//慢 退

int PlayerService::backward(bool slow)
{
    if(PlayerRunning == getStatus()){
        if(true == slow){
            pPlaybackWidget->Scan(PLAYBACK_RATE_FB);
        }
        else{
            pPlaybackWidget->Scan(PLAYBACK_RATE_SB);
        }
    }
    return 0;
}

//------------------------------------------------
//把播放文件定位到新位置
//step为重定位的步长， 正数为像前定位，负数为向后定位,单位为毫秒
int PlayerService::seek(u64 step)
{
    pPlaybackWidget->Seek(step * 1000, false);
    return 0;
}

//------------------------------
//设置音量
//vol:音量大小（0-100）
//返回值： 0成功，-1失败

int PlayerService::setVolume(int vol)
{
    if(0 <= vol && 100 >= vol){
        pPlaybackWidget->SetVolume(vol);
        return 0;
    }
    else{
        return -1;
    }
}

//------------------------------------------------
//得到当前音量
//返回值为当前音量大小， 范围为0－100
int PlayerService::getVolume()
{
    return pPlaybackWidget->GetVolume();
}

//------------------------------------------------
//得到当前播放状态
//返回值包括：4 status: stop/pause/running/scan
//此函数是非堵塞函数，调用以后立即返回
PlayerStatus PlayerService::getStatus()
{
    return (PlayerStatus)pPlaybackWidget->GetState();
}

//------------------------------------------------
//得到当前播放时长
//回传
int PlayerService::getPosition(u64* pPos)
{
    pPlaybackWidget->GetTime(pPos);
    return 0;
}

//------------------------------------------------
//得到视频文件总时长
//回传
int PlayerService::getTotal(u64* pTot)
{
    pPlaybackWidget->GetDuration(pTot);
    return 0;
}

void PlayerService::playerFinished( ){

    emit playFinished( mPath );
}

#undef CALIBRATIONTICK

#endif
